home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Sample Code Update 01⁄96 / Fragment Tool / Sources / Lists.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-20  |  13.4 KB  |  585 lines  |  [TEXT/MPCC]

  1. /*
  2.     File:        Lists.c
  3.  
  4.     Contains:    List Manager stuff and associated routines
  5.  
  6.     Written by:    Chris White, Developer Technical Support
  7.     
  8.     Copyright:    © 1995 by Apple Computer, Inc., all rights reserved.
  9.     
  10.     Change History (most recent first):
  11.     
  12.                   9/28/95    CW        First release
  13.  
  14. */
  15.  
  16. #include <Drag.h>
  17. #include <Errors.h>
  18. #include <MixedMode.h>
  19.  
  20.  
  21. #include "MenuStuff.h"
  22.  
  23. #include "Prototypes.h"
  24.  
  25.  
  26. static Boolean UserWantsToDrag ( ListRef theList, Point globallPt );
  27. static Boolean MouseHitSelection ( ListRef theList, Point localPt );
  28. static Boolean EqualCells ( Cell c1, Cell c2 );
  29. static void HandleClickInList ( WindowRef theWindow, Boolean newClick, 
  30.                                 Boolean doubleClick, Cell newCell );
  31. static Boolean GetSelectionRect ( ListRef theList, Rect* theRect );
  32. static OSErr StartADrag ( WindowRef theWindow, EventRecord* theEvent );
  33. static OSErr AddFlavors ( DragReference theDrag, ItemReference theItem, WindowRef theWindow );
  34.  
  35. static void OutlineRegion ( RgnHandle theRgn );
  36. static void LocalToGlobalRect ( Rect* theRect );
  37.  
  38. static Boolean MyClickLoop ( void );
  39.  
  40.  
  41.  
  42.  
  43.  
  44. // We'll limit its scope to this file
  45. static ListRef gCurrentClickedList = nil;
  46.  
  47.  
  48. //
  49. // Gotcha: The List Manager doesn't actually check D0, but rather simply tests the Z-bit in
  50. // the 68K processor's status register. Because this is lost during Mixed Mode, we need to
  51. // use a 68K stub routine that calls the native clickLoop and then tests DO before returning
  52. // to the List Manager. The emulator will set its Z-bit, and the List Manager will be happy.
  53. // See the ReadMe file for a more complete explanation.
  54. //
  55. void InitListClickLoop ( void )
  56. {
  57.  
  58. #ifdef powerc
  59.  
  60.     static RoutineDescriptor theListClickLoopRD =
  61.                 BUILD_ROUTINE_DESCRIPTOR ( uppListClickLoopProcInfo, MyClickLoop );
  62.     
  63.     
  64.     #ifdef powerc
  65.     #pragma options align=mac68k
  66.     #endif
  67.     
  68.     static struct LClickLoopGlue
  69.     {
  70.        long     move;                    // MOVEA.L  ClickLoopUPP, A0
  71.        short    jsr;                    // JSR  (A0)
  72.        short    tst;                    // TST.B D0
  73.        short    rts;                    // RTS
  74.        UniversalProcPtr ClickLoopUPP;    // Storage for the UPP
  75.     } LClickLoop68K = {
  76.        0x207A0008,
  77.        0x4E90,
  78.        0x4A00,
  79.        0x4E75,
  80.        (UniversalProcPtr) &theListClickLoopRD
  81.     };
  82.     
  83.     #ifdef powerc
  84.     #pragma options align=reset
  85.     #endif
  86.     
  87.     gClickLoopUPP = (ListClickLoopUPP) &LClickLoop68K;
  88.     
  89. #else
  90.     gClickLoopUPP = NewListClickLoopProc ( MyClickLoop );
  91. #endif
  92.     
  93.     return;
  94. }
  95.  
  96.  
  97.  
  98. Boolean PtInList ( Point localPt, ListRef theList )
  99. {
  100.     return PtInRect ( localPt, &(*theList)->rView );
  101. }
  102.  
  103.  
  104.  
  105. void GetListRect ( Rect* theRect, ListRef theList )
  106. {
  107.     *theRect = (*theList)->rView;
  108. }
  109.  
  110.  
  111.  
  112. void AddToList ( ListRef theList, Str255 theString )
  113. {
  114.     Rect    dataRect;
  115.     Cell    cell;
  116.     short    nuRow;
  117.     short    stringSize = sizeof(OSType);
  118.     
  119.     #if DEBUGGING
  120.     if ( theList == nil ) DebugStr ( "\p theList == nil");
  121.     #endif
  122.     
  123.     dataRect = (*theList)->dataBounds;
  124.     nuRow = LAddRow ( 1, dataRect.bottom, theList );
  125.     SetPt ( &cell, 0, nuRow );
  126.     LSetCell ( &theString[1], theString[0], cell, theList );
  127.     
  128.     return;
  129. }
  130.  
  131.  
  132.  
  133. void UpdateList ( ListRef theList, int16 theIndex, Str255 theString )
  134. {
  135.     Cell    theCell;
  136.     
  137.     SetPt ( &theCell, 0, theIndex );
  138.     LSetCell ( &theString[1], theString[0], theCell, theList );
  139.     
  140.     return;
  141. }
  142.  
  143.  
  144.  
  145. void DeleteFromList ( ListRef theList, int16 theIndex )
  146. {
  147.     LDelRow ( 1, theIndex, theList );
  148.     
  149.     return;
  150. }
  151.  
  152.  
  153. /*------------------------------------------------------------------*/
  154.  
  155.  
  156.  
  157. Boolean HandleListClick ( WindowRef theWindow, EventRecord* event )
  158. {
  159.     Boolean        bWasHandled = false;
  160.     Point        localPt;
  161.     Rect        listRect;
  162.     ListRef    theList;
  163.     
  164.     
  165.     
  166.     localPt = event->where;
  167.     GlobalToLocal ( &localPt );
  168.     theList = GetWListRef ( theWindow );
  169.     
  170.     
  171.     if ( (*theList)->lActive == true )
  172.     {
  173.         listRect = (*theList)->rView;
  174.         listRect.right += 15; listRect.bottom += 15;
  175.         
  176.         if ( PtInRect ( localPt, &listRect ) )
  177.         {    
  178.             Cell        oldCell, newCell;
  179.             Boolean        oldSelection, 
  180.                         validSelection = false, 
  181.                         newClick = false, 
  182.                         doubleClick;
  183.             
  184.             
  185.             oldSelection = GetFirstSelection ( theList, &oldCell );
  186.             
  187.             
  188.             
  189.             // UserWantsToDrag takes a point in global coords. It uses the Drag Manager
  190.             // routine WaitMouseMoved to make sure the user is dragging as opposed
  191.             // to double clicking. If a drag is started when the user is really trying
  192.             // to double click, it will end up being a very quick drag.
  193.             
  194.             if ( UserWantsToDrag ( theList, event->where ) )
  195.             {
  196.                 
  197.                 // The click loop code needs to wait until the mouse is moved out
  198.                 // of the list rect before starting a drag. Because the default
  199.                 // behaviour of a a drag within a list is to change the selection
  200.                 // and, if required, scroll the list, attempting to drag a cell
  201.                 // by moving the mouse over another cell will result in that cell
  202.                 // being hilited. The job of this code is to detect those attempted
  203.                 // drags and call the necessary drag routines instead of making a
  204.                 // call to the LClick routine.
  205.                 
  206.                 
  207.                 // If we successfully drop the cell somewhere else,
  208.                 // clear the selection in the original list.
  209.                 
  210.                 if ( StartADrag ( theWindow, event ) == noErr )
  211.                     LSetSelect ( false, oldCell, theList );
  212.                 
  213.                 return true;
  214.                 
  215.             }
  216.                         
  217.             //
  218.             // Save the current list for use in our click loop 
  219.             //
  220.             gCurrentClickedList = theList;
  221.             doubleClick = LClick ( localPt, event->modifiers, theList );
  222.             
  223.             validSelection = GetFirstSelection ( theList, &newCell );
  224.             if ( validSelection ) 
  225.                 if ( !oldSelection || !EqualCells ( newCell, oldCell ) )
  226.                     newClick = true;
  227.             
  228.             HandleClickInList ( theWindow, newClick, doubleClick, newCell );
  229.             bWasHandled = true;
  230.         }
  231.     }
  232.     
  233.     return bWasHandled;
  234. }
  235.  
  236.  
  237.  
  238. Boolean GetSelection ( ListRef theList, int16* theIndex )
  239. {
  240.     Boolean        bFound;
  241.     Cell        theCell;
  242.     
  243.     SetPt ( (Point*) &theCell, 0, *theIndex );
  244.     bFound = LGetSelect ( true, &theCell, theList );
  245.     if ( bFound )
  246.         *theIndex = theCell.v;
  247.     
  248.     return bFound;
  249. }
  250.  
  251.  
  252.  
  253. Boolean GetFirstSelection ( ListRef theList, Cell* theCell )
  254. {
  255.     SetPt ( (Point*) theCell, 0, 0 );
  256.     return LGetSelect ( true, theCell, theList );
  257. }
  258.  
  259.  
  260.  
  261. //
  262. // Does the user want to drag something? First, checks the click could
  263. // be drag, then waits to see if the user starts to drag the cell.
  264. // 
  265. static Boolean UserWantsToDrag ( ListRef theList, Point globalPt )
  266. {
  267.     Point    localPt;
  268.     
  269.     localPt = globalPt;
  270.     GlobalToLocal ( &localPt );
  271.     if ( MouseHitSelection ( theList, localPt ) )
  272.         return WaitMouseMoved ( globalPt );
  273.         
  274.     return false;
  275. }
  276.  
  277.  
  278.  
  279. //
  280. // Has the user clicked on a select cell?
  281. //
  282. static Boolean MouseHitSelection ( ListRef theList, Point localPt )
  283. {
  284.     Cell        selectedCell;
  285.     Rect        selectedCellRect;
  286.     
  287.     
  288.     if ( !(GetFirstSelection ( theList, &selectedCell )) )
  289.         return false;
  290.  
  291.     if ( !(PtInRect ( *(Point*) &selectedCell, (Rect*) &(*theList)->visible )) )
  292.         return false;
  293.  
  294.     if ( !(GetSelectionRect ( theList, &selectedCellRect )) )
  295.         return false;
  296.     
  297.     return PtInRect ( localPt, &selectedCellRect );
  298. }
  299.  
  300.  
  301.  
  302. static Boolean EqualCells ( Cell c1, Cell c2 )
  303. {
  304.     return (c1.h == c2.h && c1.v == c2.v);
  305. }
  306.  
  307.  
  308.  
  309. //
  310. // This routine is called in response to a click in a list. We'll
  311. // turn a double click into a “Get Info” command.
  312. //  
  313. static void HandleClickInList ( WindowRef theWindow, Boolean newClick, 
  314.                                 Boolean doubleClick, Cell newCell )
  315. {
  316.     if ( doubleClick )
  317.     {
  318.         int32 menuSelection;
  319.         
  320.         menuSelection = kFragmentMenu << 16;
  321.         menuSelection += cGetInfo;
  322.         MenuDispatch ( menuSelection );
  323.     }
  324.     
  325.     return;
  326. }
  327.  
  328.  
  329.  
  330. //
  331. // This gets a rectangle for the selection in a list. It's used to
  332. // pass to the Drag Manager for user feedback.
  333. //
  334. static Boolean GetSelectionRect ( ListRef theList, Rect* theRect )
  335. {
  336.     Boolean    validSelection;
  337.     Cell    selectedCell, adjustedCell;
  338.     Rect    selectedCellRect, emptyRect = {0, 0, 0, 0};
  339.     
  340.     *theRect = emptyRect;
  341.     validSelection = GetFirstSelection ( theList, &selectedCell );
  342.     
  343.     if ( validSelection )
  344.     {
  345.         
  346.         //
  347.         // adjustedCell is needed because the current view of the list may not
  348.         // have cell (0, 0) at the top left corner. This takes the top and left
  349.         // values of the visible field and subtracts them from the selected cell.
  350.         // We end up with this cell's location in the current view honoring scroll
  351.         // bars.
  352.         //
  353.         
  354.         adjustedCell.v = selectedCell.v - (*theList)->visible.top;
  355.         adjustedCell.h = selectedCell.h - (*theList)->visible.left;
  356.         
  357.         selectedCellRect.top = (*theList)->rView.top + (adjustedCell.v * (*theList)->cellSize.v);
  358.         selectedCellRect.bottom = selectedCellRect.top + (*theList)->cellSize.v;
  359.         
  360.         
  361.         //
  362.         // Because the cell width is 32767 (see comments in CreateContentList routine),
  363.         // we need to use the view width instead of the cell width. If we don't, the Drag
  364.         // Manager will not draw anything as the user drags the cell around (because it's
  365.         // too big). Even if the Drag Manager did do any drawing, it wouldn't reflect the
  366.         // size of the cell as the user sees it.
  367.         //
  368.         
  369.         selectedCellRect.left = (*theList)->rView.left + (adjustedCell.h * (*theList)->rView.right);
  370.         selectedCellRect.right = selectedCellRect.left + (*theList)->rView.right;
  371.         
  372.         *theRect = selectedCellRect;
  373.     }
  374.     
  375.     return validSelection;
  376. }
  377.  
  378.  
  379.  
  380. //
  381. // This creates a new drag for the Drag Manager and tracks it until
  382. // the user is done. If a successful drop is made, the Drag Manager
  383. // will call our DragReceiver routine.
  384. //
  385. static OSErr StartADrag ( WindowRef theWindow, EventRecord* theEvent )
  386. {
  387.     OSErr            theErr = noErr;
  388.     DragReference    theDrag;
  389.     Rect            dragBounds;
  390.     RgnHandle        dragRgn;
  391.     ListRef        theList;
  392.     ItemReference    theItem = 1;
  393.     
  394.     
  395.     if ( !gHasDragManager )
  396.         return kDragManagerNotPresent;
  397.     
  398.     
  399.     theList = GetWListRef ( theWindow );
  400.     
  401.     theErr = NewDrag ( &theDrag );
  402.     if ( theErr == noErr )
  403.     {        
  404.         theErr = AddFlavors ( theDrag, theItem, theWindow );
  405.  
  406.         if ( theErr == noErr )
  407.         {
  408.             
  409.             if ( GetSelectionRect ( theList, &dragBounds ) )
  410.             {
  411.                 LocalToGlobalRect ( &dragBounds );                
  412.                 theErr = SetDragItemBounds ( theDrag, theItem, &dragBounds );
  413.  
  414.                 if ( theErr == noErr )
  415.                 {    
  416.                     dragRgn = NewRgn ( );
  417.                     RectRgn ( dragRgn, &dragBounds );
  418.                     OutlineRegion ( dragRgn );
  419.                     theErr = TrackDrag ( theDrag, theEvent, dragRgn );
  420.                     
  421.                     DisposeRgn ( dragRgn );
  422.                 }
  423.             }
  424.         }
  425.         
  426.         DisposeDrag ( theDrag );
  427.     }
  428.  
  429.     return theErr;
  430. }
  431.  
  432.  
  433.  
  434. static Boolean MyClickLoop ( void )
  435. {
  436.     Point        localPt;
  437.     Cell        selectedCell;
  438.     WindowRef    theWindow;
  439.     ListRef    list;
  440.     
  441.     
  442.     
  443.     //
  444.     // This code starts a drag for those cases where there isn't already
  445.     // a selection, and the mouse is dragged out in such a way as to not
  446.     // cause another cell to be hilited. We need to wait until the mouse
  447.     // is dragged out of the list rect for two reasons. First, to allow
  448.     // the default behaviour to occur. Second, to stop a single click from
  449.     // acting like the current selection was dragged and release where the
  450.     // click was made. Note that the default behaviour is to change the
  451.     // selection as the mouse is dragged over the cells, and scroll the
  452.     // list if required.
  453.     // 
  454.     
  455.     
  456.     theWindow = FrontWindow ( );
  457.     list = GetWListRef ( theWindow );
  458.     
  459.     GetMouse ( &localPt );    
  460.     if ( GetFirstSelection ( list, &selectedCell ) )
  461.     {
  462.         if ( !(PtInRect ( localPt, &(*list)->rView)) )
  463.         {
  464.             EventRecord    dummyEvent;
  465.             long        tmpLong;
  466.             Rect        tmpRect;
  467.             
  468.             OSEventAvail(0, &dummyEvent);
  469.             dummyEvent.what = mouseDown;
  470.             
  471.             //
  472.             // We go through the trouble of making sure the mouse doesn't
  473.             // appear outside of the list cell region when the drag starts.
  474.             //
  475.             
  476.             GetSelectionRect(list, &tmpRect);
  477.             InsetRect(&tmpRect, 2, 2);
  478.             tmpLong = PinRect(&tmpRect, localPt);
  479.             dummyEvent.where = *(Point *) &tmpLong;
  480.             LocalToGlobal((Point *) &dummyEvent.where);
  481.             
  482.             //
  483.             // If we successfully drop the cell somewhere else,
  484.             // clear the selection in the original list.
  485.             //
  486.             
  487.             if ( StartADrag ( theWindow, &dummyEvent ) == noErr )
  488.                 LSetSelect ( false, selectedCell, list );
  489.             
  490.             return false;
  491.             
  492.         }
  493.     }
  494.  
  495.     return true;
  496. }
  497.  
  498.  
  499.  
  500. //
  501. // This tells the Drag Manager what data to send when the user drags something
  502. //
  503. static OSErr AddFlavors ( DragReference theDrag, ItemReference theItem, WindowRef theWindow )
  504. {
  505.     OSErr        theErr = noErr;
  506.     int16        theIndex = 0;
  507.     Size        theSize;
  508.     ListRef        theList;
  509.     tDragData    theData;
  510.     
  511.     
  512.     // First, we'll promise to create a file if the user drags
  513.     // something out to the Finder.
  514.     theErr = AddHFSPromise ( theDrag, theItem );
  515.     if ( theErr )
  516.         return theErr;
  517.     
  518.     // Set the send procedure that will deliver any promised data.
  519.     SetDragSendProc ( theDrag, gDragSendDataProcUPP, theWindow );
  520.     
  521.     
  522.     
  523.     //
  524.     // I'm being lazy here. There's no real advantage to including
  525.     // the window reference with each item (well, apart from the effort
  526.     // saved on my part).
  527.     //
  528.     
  529.     theList = GetWListRef ( theWindow );
  530.     theData.theWindow = theWindow;
  531.     theSize = sizeof ( tDragData );
  532.     
  533.     while ( GetSelection ( theList, &theIndex ) )
  534.     {
  535.         OSErr        theErr;
  536.         
  537.         //
  538.         // The flavorSenderOnly is used to tell the Drag Manager that this data will not
  539.         // be valid in another client. Eg. If this application was launched twice, one
  540.         // could accept a drag from the other. However, since the data consists of ptrs
  541.         // rather than the actual data, it wouldn't make sense. Note, this doesn't affect
  542.         // dragging to the finder, which is a special case.
  543.         //
  544.         theData.theIndex = theIndex;
  545.         theErr = AddDragItemFlavor ( theDrag, theItem, kCreatorCode, 
  546.                                     (Ptr) &theData, theSize, flavorSenderOnly );
  547.         if ( theErr )
  548.             return theErr;
  549.             
  550.         theIndex++;
  551.     }
  552.     
  553.     
  554.     return noErr;
  555. }
  556.  
  557.  
  558.  
  559. static void OutlineRegion ( RgnHandle theRgn )
  560. {
  561.     RgnHandle tempRgn;
  562.     
  563.     tempRgn = NewRgn ( );
  564.     CopyRgn ( theRgn, tempRgn );
  565.     InsetRgn ( tempRgn, 1, 1 );
  566.     DiffRgn ( theRgn, tempRgn, theRgn );
  567.     DisposeRgn ( tempRgn );
  568.     
  569.     return;
  570. }
  571.  
  572.  
  573.  
  574. static void LocalToGlobalRect ( Rect* theRect )
  575. {
  576.     LocalToGlobal ( (Point*) &theRect->top );
  577.     LocalToGlobal ( (Point*) &theRect->bottom );
  578.     
  579.     return;
  580. }
  581.  
  582.  
  583.  
  584.  
  585.